package com.bytecub.simulate.device.instance;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSONObject;
import com.bytecub.common.constants.BCConstants;
import com.bytecub.common.domain.bo.BaseAttrItemBo;
import com.bytecub.common.domain.dto.response.prop.TemplateResDto;
import com.bytecub.common.domain.gateway.mq.UpgradeMessageBo;
import com.bytecub.common.domain.message.DeviceDownMessage;
import com.bytecub.common.metadata.BcMetaType;
import com.bytecub.simulate.device.common.LogContext;
import com.bytecub.simulate.device.common.domain.FuncItem;
import com.bytecub.simulate.device.mqtt.SimulateMqttClient;
import com.bytecub.simulate.device.task.DeviceReporterTask;
import com.bytecub.utils.JSONProvider;
import com.bytecub.utils.file.Base64FileUtil;
import org.bouncycastle.cert.dane.DANEEntry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.lang.reflect.Executable;
import java.time.LocalDateTime;
import java.util.*;

/**
  * ByteCub.cn.
  * Copyright (c) 2020-2021 All Rights Reserved.
  * 
  * @author bytecub@163.com  songbin
  * @version Id: DeviceInstance.java, v 0.1 2021-01-09  Exp $$
  */
public class DeviceInstance {
    private static final Logger logger = LoggerFactory.getLogger(DeviceInstance.class);
    public static Snowflake snowflake = IdUtil.getSnowflake(1, 1);
    List<FuncItem> deviceFunc;
    SimulateMqttClient mqttClient;

    public String getDeviceCode() {
        return deviceCode;
    }

    String deviceCode;
    String deviceSecret;
    String productCode;
    String mqttUrl;
    List<String> topics;
    /**设备属性上报*/
    String propertyTopic ;
    /**设备事件上报*/
    String eventTopic ;
    /**服务调用结果上报*/
    String messageReplyTopic;
    /**属性值读取上报*/
    String propertyReaderTopic;
    /**升级指定上报*/
    String upgradeReplyTopic;
    /**订阅升级*/
    String upgradeTopic;
    public TemplateResDto eventDemo;
    public TemplateResDto propDemo;
    public DeviceInstance(List<FuncItem> deviceFunc, String deviceCode, String deviceSecret,
                          String productCode, String mqttUrl, TemplateResDto eventDemo, TemplateResDto propDemo) {
        this.deviceFunc = deviceFunc;
        this.deviceCode = deviceCode;
        this.deviceSecret = deviceSecret;
        this.productCode = productCode;
        this.mqttUrl = mqttUrl;
        this.eventDemo = eventDemo;
        this.propDemo = propDemo;
    }

    public void genClient(){
        try {
            this.mqttClient = new SimulateMqttClient(this, mqttUrl, deviceCode, productCode, deviceSecret);
            List<String> topics = this.buildTopics(deviceCode,productCode);
            this.mqttClient.genClient();
            this.mqttClient.subscribe(topics);
            this.buildUpTopics(deviceCode, productCode);
            DeviceReporterTask deviceReporterTask = new DeviceReporterTask(deviceCode, this);
            Thread thread = new Thread(deviceReporterTask);
            thread.start();
        } catch (Exception e) {
            logger.warn("", e);
            throw new RuntimeException(e);
        }
    }
    /**属性和事件上报*/
    public void reportData(){
        try{
            String propDemo = this.buildMsg(this.propDemo);
            String eventDemo = this.buildMsg(this.eventDemo);
            logger.info("topic:{}   data:{}", this.propertyTopic, propDemo);
            this.mqttClient.publish(this.propertyTopic, propDemo.getBytes("GB2312"));
            this.mqttClient.publish(this.eventTopic, eventDemo.getBytes("GB2312"));
            logger.info("logIndex:{}, threadid:{}, 上报完成，设备ID:{}，内容：{}",LogContext.CONTEXT.get(), Thread.currentThread().getId(), deviceCode, propDemo);
        }catch (Exception e){
            logger.warn("异常", e);
        }
    }

    private String buildMsg(TemplateResDto templateResDto){
        Map<String, Object> demoResult = this.rebuildDemo(templateResDto);
        return JSONProvider.toJSONString(demoResult);
    }
    /**
     * 按照数据模板生成示例数据
     * */
    private Map<String, Object> rebuildDemo(TemplateResDto templateResDto){
        Map<String, Object> demoResult = new HashMap<>();
        Map<String, Object> template = templateResDto.getTemplate();
        Map<String, Object> demo = templateResDto.getDemoData();

        demo.forEach((key, value)->{
            String attr = JSONProvider.toJSONString(template.get(key));
            BaseAttrItemBo baseAttrItemBo = JSONProvider.parseObject(attr, BaseAttrItemBo.class);
            BcMetaType metaType = BcMetaType.explain(baseAttrItemBo.getDataType());
            switch (metaType){
                case DATE:
                    demoResult.put(key, System.currentTimeMillis());
                    break;
                case STRING:
                    String valueData = new StringBuilder("设备:").append(this.deviceCode).append(" ").append("ThreadId: ").append(Thread.currentThread().getId())
                            .append(" logId: ").append(LogContext.CONTEXT.get()).append(" 时间：").append(LocalDateTime.now()).toString();
//                    String valueData = "设备:" + this.deviceCode + " " + LocalDateTime.now()+ " ThreadId: " + Thread.currentThread().getId()
                    demoResult.put(key, valueData);
                    break;
                default:
                    demoResult.put(key, value);
            }
        });
        return demoResult;
    }
    /**
     * 属性读取上报
     * */
    public void PropertyReaderReply(String identifier, String messageId){
        try{
            Object value = this.propDemo.getDemoData().get(identifier);
            Map<String, Object> resp = new HashMap<>();
            resp.put("messageId", messageId);
            Map<String, Object> respVal = new HashMap<>();
            respVal.put(identifier, value);
            resp.put("value", respVal);

            String valueStr = JSONProvider.toJSONString(resp);
            this.mqttClient.publish(this.propertyReaderTopic, valueStr.getBytes(BCConstants.GLOBAL.CHARSET_GB2312));
        }catch (Exception e){
            logger.warn("异常", e);
        }

    }

    /**
     * 属性读取上报
     * */
    public void PropertyWriterReply(String messageId){
        try{
            Map<String, Object> resp = new HashMap<>();
            resp.put("messageId", messageId);
            Map<String, Object> respVal = new HashMap<>();
            respVal.put("code", 200);
            respVal.put("msg", "成功");
            resp.put("result", respVal);
            String valueStr = JSONProvider.toJSONString(resp);
            this.mqttClient.publish(this.messageReplyTopic, valueStr.getBytes(BCConstants.GLOBAL.CHARSET_GB2312));
        }catch (Exception e){
            logger.warn("异常", e);
        }

    }
    /**
     * 固件升级上报
     * */
    public void UpgradeReply(DeviceDownMessage msg){
        try{
            Map<String, Object> resp = new HashMap<>();
            resp.put("messageId", msg.getMessageId());
            Map<String, Object> respVal = new HashMap<>();
            respVal.put("code", 2);
            respVal.put("msg", "成功");
            resp.put("result", respVal);
            //bytecub.firmware.dir
            UpgradeMessageBo bo = JSONProvider.parseJsonObject((JSONObject) msg.getBody(), UpgradeMessageBo.class);
            Base64FileUtil.generateFile(bo.getFileBase64(), "./upload/" + UUID.randomUUID().toString());
            String valueStr = JSONProvider.toJSONString(resp);
            this.mqttClient.publish(this.upgradeReplyTopic, valueStr.getBytes(BCConstants.GLOBAL.CHARSET_GB2312));
        }catch (Exception e){
            logger.warn("异常", e);
        }

    }
    private List<String> buildTopics(String deviceCode, String productCode){
        //调用设备服务
        String invoke = BCConstants.MQTT.GLOBAL_DOWN_PREFIX + productCode + "/" + deviceCode + "/service/invoke";
        //设备属性设置
        String propertySet = BCConstants.MQTT.GLOBAL_DOWN_PREFIX + productCode + "/" + deviceCode + "/property/set";
        //设备属性读取
        String proertyReader = BCConstants.MQTT.GLOBAL_DOWN_PREFIX + productCode + "/" + deviceCode + "/property/get";
        String upgradeSet = BCConstants.MQTT.GLOBAL_DOWN_PREFIX + productCode + "/" + deviceCode + "/upgrade/set";
        List<String> topics = new ArrayList<>();
        topics.add(proertyReader);
        topics.add(propertySet);
        topics.add(invoke);
        topics.add(upgradeSet);
        return topics;
    }
    private void buildUpTopics(String deviceCode, String productCode){
        //属性上报
        this.propertyTopic = BCConstants.MQTT.GLOBAL_UP_PREFIX + productCode + "/" + deviceCode + "/prop";
        //事件上报
        this.eventTopic = BCConstants.MQTT.GLOBAL_UP_PREFIX + productCode + "/" + deviceCode + "/event";
        //服务调动结果上报
        this.messageReplyTopic = BCConstants.MQTT.GLOBAL_UP_PREFIX + productCode + "/" + deviceCode + "/message/reply";
        //设备属性读取响应
        this.propertyReaderTopic = BCConstants.MQTT.GLOBAL_UP_PREFIX + productCode + "/" + deviceCode + "/property/getreply";
        this.upgradeReplyTopic = BCConstants.MQTT.GLOBAL_UP_PREFIX + productCode + "/" + deviceCode + "/upgrade/reply";
        //this.upgradeTopic = BCConstants.MQTT.GLOBAL_UP_PREFIX + productCode + "/" + deviceCode + "/upgrade/reply";
    }

}
